<?php

declare(strict_types=1);

namespace App\Service\Client;

use App\Constants\DefaultValue;
use App\Constants\ErrorCode;
use App\Constants\RedisKey;
use App\Dao\CooperativeDao;
use App\Dao\EmployerBalanceDao;
use App\Dao\UserDao;
use App\Dao\UserEmployerDao;
use App\Exception\BusinessException;
use App\Model\Cooperative;
use App\Model\EmployerBalance;
use App\Model\PaymentLog;
use App\Model\User;
use App\Model\UserEmployer;
use App\Utils\Lock;
use App\Utils\Log;
use App\Utils\Wechat\Payment;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\RedisFactory;
use App\Constants\Model\Cooperative as CooperativeConstants;
use App\Constants\Model\PaymentLog as PaymentLogConstants;
use App\Constants\Model\EmployerBalance as EmployerBalanceConstants;
use App\Constants\Model\UserEmployer as UserEmployerConstants;


class  EmployerService extends BaseService
{
    protected $redis;

    /**
     * @Inject()
     * @var UserDao
     */
    protected $userDao;

    /**
     * @Inject()
     * @var UserEmployerDao
     */
    protected $userEmployerDao;

    /**
     * @Inject()
     * @var CooperativeDao
     */
    protected $cooperativeDao;

    /**
     * @Inject()
     * @var EmployerBalanceDao
     */
    protected $employerBalanceDao;


    public function __construct()
    {
        $this->redis = di(RedisFactory::class)->get('default');
    }

    public function info()
    {
        $data = $this->userInfo()->employer;
        $data['nickname'] = $this->userInfo()->nickname;
        return [
            'employer' => $data
        ];
    }

    /**
     * 运营商列表
     */
    public function cooperativeList(): array
    {
        /** @var User $users */
        $users = $this->userDao
            ->where(['phone' => $this->userInfo()->phone])
            ->with('employer')
            ->get();

        $cooperativeIds = [];
        /** @var User $v */
        foreach ($users as $v) {
            //悬赏主，且未被封
            if (isset($v->employer->status) && $v->employer->status == UserEmployerConstants::STATUS_UNBLOCK) {
                $cooperativeIds[] = $v->cooperative_id;
            }
        }

        //未冻结平台
        /** @var Cooperative $cooperatives */
        $cooperatives = $this->cooperativeDao
            ->where(['ids' => $cooperativeIds, 'status' => CooperativeConstants::STATUS_UNBLOCK])
            ->select(['id', 'nickname'])
            ->get();

        return [
            'cooperatives' => $cooperatives
        ];
    }

    public function balance(array $data)
    {
        $perPage = isset($data['perPage']) ? (int)$data['perPage'] : DefaultValue::PER_PAGE_DEFAULT;

        $page = $this->employerBalanceDao
            ->where(['user_id' => $this->userInfo()->id])
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);

        return [
            'currentPage' => $page->currentPage(),
            'total' => $page->total(),//数据总数
            'hasMorePages' => $page->hasMorePages(),
            'data' => $page->items()
        ];
    }


    /**
     * @param array $data
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function rechargePrepay(array $data)
    {
        Log::get()->info('---------------------------------------1');
        if ($this->userInfo()->wx_mp_openid == '') {
            throw new BusinessException(ErrorCode::WX_OPENID_NULL);
        }

        $config = [];
        if ($this->cooperativeInfo()->bind_wx_merchant == CooperativeConstants::BIND_WX_MERCHANT_YES) {
            $config = [
                'mch_id' => $this->cooperativeInfo()->wx_merchant_code,
                'app_id' => $this->cooperativeInfo()->wx_mp_appid,
                'key' => $this->cooperativeInfo()->wx_merchant_secret,
                'sandbox' => env('SENDBOX', false),
            ];
        }

        $payment = make(Payment::class, [$config]);

        //TODO notifyUrl需要换成外网url
//        $notifyUrl = 'http://xiihii.free.idcfengye.com/rechargeCallback';
        $notifyUrl = env('RECHARGE_CALLBACK_URL');

        $money = $data['money'];
        Log::get()->info('---------------------------------------2');

        //测试环境，无论充多少钱，真实付钱只有0.01
        if (in_array(env('APP_ENV'), ['dev', 'local', 'test'])) {
            $money = '0.01';
        }

        Log::get()->info('---------------------------------------3:' . env('APP_ENV'));

        $result = $payment->prepay(
            $money,
            $payment::TRADE_TYPE_JSAPI,
            $this->userInfo()->wx_mp_openid,
            $notifyUrl
        );

        //订单关联用户
        $prepayKey = $payment->prepayKey($this->userInfo()->wx_mp_openid, $data['money'], $payment::TRADE_TYPE_JSAPI);
        $payment->setBindUser($prepayKey, $this->userInfo()->id);
        Log::get()->info('---------------------------------------4');


        //测试环境，无论充多少钱，真实付钱只有0.01
        if (in_array(env('APP_ENV'), ['dev', 'local', 'test'])) {
            $this->redis->set($prepayKey . ':money', $data['money'], 1800 + 300);
        }

        return $result;
    }

    public function rechargePrepayH5()
    {
        $config = [
            'mch_id' => '1485394602',
            'app_id' => 'wxfed5ebcc31f651d6',
            'key' => 'fadet5e532egi78754hjmyjdada63rsa',
        ];

        $payment = make(Payment::class, [$config]);

        $notifyUrl = 'http://paytest.shopping98.com';
        $result = $payment->prepay('0.01', $payment::TRADE_TYPE_MWEB, '', $notifyUrl);

        return $result;
    }

    public function rechargeCallback(array $data)
    {
        /**
         * 回调数据结构
         *
         * array(16) {
         * ["appid"]=>
         * string(18) "wx961c2d9582510b92"
         * ["bank_type"]=>
         * string(6) "OTHERS"
         * ["cash_fee"]=>
         * string(1) "1"
         * ["fee_type"]=>
         * string(3) "CNY"
         * ["is_subscribe"]=>
         * string(1) "N"
         * ["mch_id"]=>
         * string(10) "1600437881"
         * ["nonce_str"]=>
         * string(13) "5f1aa6f074814"
         * ["openid"]=>
         * string(28) "oUtPn5RQwLQ-JChFgzCp0HpxHRqs"
         * ["out_trade_no"]=>  内部订单：trade_no
         * string(18) "145215998810042368"
         * ["result_code"]=>
         * string(7) "SUCCESS"
         * ["return_code"]=>
         * string(7) "SUCCESS"
         * ["sign"]=>
         * string(32) "9482C678B248B3DD267CF85C7F9597FA"
         * ["time_end"]=>
         * string(14) "20200724171643"
         * ["total_fee"]=>
         * string(1) "1"
         * ["trade_type"]=>
         * string(5) "JSAPI"
         * ["transaction_id"]=>    外部订单：out_trade_no
         * string(28) "4200000709202007244109252262"
         * }
         */
        $responseText = '<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>';
        $payment = di(Payment::class);
        $prepayKey = $payment->prepayKey($data['openid'], $data['total_fee'] / 100, $data['trade_type']);

        $prepayValue = $this->redis->get($prepayKey);
        if (!$prepayValue) {
            //订单不存在
            return $responseText;
        }

        $Lock = new Lock();
        $lockKeyPrepay = RedisKey::PAYMENT . $prepayValue;
        $lockStatePrepay = $Lock->lock($lockKeyPrepay, DefaultValue::LOCK_TTL);
        if (!$lockStatePrepay) {
            return '';
        }

        $userId = $payment->getBindUser($prepayKey);

        /** @var User $user */
        $user = $this->userDao->first(['id' => $userId]);

        $lockKeyEmployer = RedisKey::LOCK_MYSQL_TABLE_ID . 'user_employer:' . $user->employer->id;
        $lockStateEmployer = $Lock->lock($lockKeyEmployer, DefaultValue::LOCK_TTL);

        if (!$lockStateEmployer) {
            $Lock->unlock($lockKeyPrepay);
            return '';
        }

        /** @var UserEmployer $employer */
        $employer = $this->userEmployerDao->first(['id' => $user->employer->id]);

        Db::beginTransaction();
        try {
            $money = bcdiv((string)$data['total_fee'], (string)100, 2);
            //悬赏主账户
            $employer->balance = bcadd((string)$employer->balance, (string)$money, 2);
            $employer->save();

            $isSelfMerchant = PaymentLogConstants::IS_SELF_MERCHANT_NO;
            if ($user->cooperative->bind_wx_merchant == CooperativeConstants::BIND_WX_MERCHANT_YES) {
                $isSelfMerchant = PaymentLogConstants::IS_SELF_MERCHANT_YES;
            }


            if (in_array(env('APP_ENV'), ['dev', 'local'])) {
                $moneyTest = $this->redis->get($prepayKey . ':money');
                $money = bcdiv((string)$moneyTest, (string)100, 2);
            }

            //充值流水
            $paymentLog = new PaymentLog();
            $paymentLog->user_id = $user->id;
            $paymentLog->cooperative_id = $user->cooperative_id;
            $paymentLog->type = PaymentLogConstants::TYPE_WXPAY;
            $paymentLog->trade_no = $data['out_trade_no'];
            $paymentLog->out_trade_no = $data['transaction_id'];
            $paymentLog->money = $money;
            $paymentLog->is_self_merchant = $isSelfMerchant;
            $paymentLog->save();


            //悬赏主账户流水
            $employerBalance = new EmployerBalance();
            $employerBalance->user_id = $user->id;
            $employerBalance->cooperative_id = $user->cooperative_id;
            $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_RECHARGE;
            $employerBalance->money = $money;
            $employerBalance->detail = EmployerBalanceConstants::ACCOUNT_TYPE_TEXT[EmployerBalanceConstants::ACCOUNT_TYPE_RECHARGE];
            $employerBalance->save();

            Db::commit();

            $payment->delBindUser($prepayKey);
            $payment->release($prepayKey);
            $Lock->unlock($lockKeyPrepay);
            $Lock->unlock($lockKeyEmployer);

            Log::get()->info(json_encode($data));
            return $responseText;
        } catch (\Throwable $e) {
            $Lock->unlock($lockKeyPrepay);
            $Lock->unlock($lockKeyEmployer);
            Db::rollBack();
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());
            return '';
        }
    }

}